/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2021 Marcus Britanicus (https://gitlab.com/marcusbritanicus)
 * Copyright (c) 2021 Abrar (https://gitlab.com/s96Abrar)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 **/

#include <QObject>
#include <wayland-client.h>

#include "wayqt/WayQtUtils.hpp"
#include "wayqt/SessionLock.hpp"

#include "ext-session-lock-v1-client-protocol.h"

WQt::SessionLockManager::SessionLockManager( ext_session_lock_manager_v1 *lockMgr ) {
    mObj = lockMgr;
}


WQt::SessionLockManager::~SessionLockManager() {
    ext_session_lock_manager_v1_destroy( mObj );
}


WQt::SessionLock *WQt::SessionLockManager::lock() {
    ext_session_lock_v1 *lock = ext_session_lock_manager_v1_lock( mObj );

    return new WQt::SessionLock( lock );
}


ext_session_lock_manager_v1 *WQt::SessionLockManager::get() {
    return mObj;
}


WQt::SessionLock::SessionLock( ext_session_lock_v1 *sessLock ) {
    mObj = sessLock;
}


WQt::SessionLock::~SessionLock() {
    ext_session_lock_v1_destroy( mObj );
}


void WQt::SessionLock::setup() {
    /** Start listening to the events of ext_session_lock_v1 */
    ext_session_lock_v1_add_listener( mObj, &mListener, this );
}


bool WQt::SessionLock::isLocked() {
    return mLocked;
}


WQt::SessionLockSurface * WQt::SessionLock::getLockSurface( QWindow *window, wl_output *output ) {
    /** Convert QWindow to wl_surface */
    wl_surface *wlSurf = WQt::Utils::wlSurfaceFromQWindow( window );

    /** Get the lock surface */
    ext_session_lock_surface_v1 *lockSurf = ext_session_lock_v1_get_lock_surface( mObj, wlSurf, output );

    return new WQt::SessionLockSurface( lockSurf );
}


void WQt::SessionLock::unlockAndDestroy() {
    if ( mLocked ) {
        ext_session_lock_v1_unlock_and_destroy( mObj );
    }
}


ext_session_lock_v1 *WQt::SessionLock::get() {
    return mObj;
}


void WQt::SessionLock::handleFinished( void *data, ext_session_lock_v1 * ) {
    WQt::SessionLock *lock = reinterpret_cast<WQt::SessionLock *>(data);

    /** Inform the listeners that the acquired lock is now destroyed */
    if ( lock->mLocked ) {
        emit lock->lockDestroyed();
    }

    /** Inform the listeners that we could not obtain the lock */
    else {
        emit lock->lockFailed();
    }

    ext_session_lock_v1_destroy( lock->mObj );
    lock->mObj    = nullptr;
    lock->mLocked = false;
}


void WQt::SessionLock::handleLocked( void *data, ext_session_lock_v1 * ) {
    WQt::SessionLock *lock = reinterpret_cast<WQt::SessionLock *>(data);

    lock->mLocked = true;

    /** Inform the listeners that we have obtained the lock */
    emit lock->lockAcquired();
}


const ext_session_lock_v1_listener WQt::SessionLock::mListener = {
    handleLocked,
    handleFinished,
};


WQt::SessionLockSurface::SessionLockSurface( ext_session_lock_surface_v1 *lockSurf ) {
    mObj = lockSurf;

    /** Start listening to the events of ext_session_lock_v1 */
    ext_session_lock_surface_v1_add_listener( mObj, &mListener, this );
}


WQt::SessionLockSurface::~SessionLockSurface() {
    ext_session_lock_surface_v1_destroy( mObj );
}


void WQt::SessionLockSurface::setup() {
    if ( mIsSetup == false ) {
        mIsSetup = true;

        if ( pendingResize.isValid() ) {
            emit resizeLockSurface( pendingResize );
        }
    }
}


ext_session_lock_surface_v1 *WQt::SessionLockSurface::get() {
    return mObj;
}


void WQt::SessionLockSurface::handleConfigure( void *data, ext_session_lock_surface_v1 *, uint32_t serial, uint32_t width, uint32_t height ) {
    WQt::SessionLockSurface *lockSurf = reinterpret_cast<WQt::SessionLockSurface *>(data);

    ext_session_lock_surface_v1_ack_configure( lockSurf->get(), serial );

    if ( lockSurf->mIsSetup == false ) {
        lockSurf->pendingResize = QSize( width, height );
    }

    else {
        emit lockSurf->resizeLockSurface( QSize( width, height ) );
    }
}


const ext_session_lock_surface_v1_listener WQt::SessionLockSurface::mListener = {
    handleConfigure
};
